/*
 * Fichier: GLinkLabel.java
 * Crée le: 28 octobre 2008.
 * Modifié: 14 février 2009.
 * Auteurs: Sébastien ESTIENNE.
 * SiteWeb: http://www.prog-info.org/
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.OF
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package javaexos.gui.common;



import java.awt.Color;
import java.awt.Cursor;
import java.awt.Desktop;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.SwingConstants;



/**
 * <p>Classe représentant un lien hypertexte.</p>
 * @author Sébastien ESTIENNE.
 */
public class GLinkLabel extends JLabel implements MouseListener
{
   /** Serial version UID. */
   private static final long serialVersionUID = 1L;

   /**
    * <p>Statut du parcours du lien.</p>
    */
   public enum BrowseStatus
   {
      /** Parcours du lien avec succès. */
      SUCCESS,

      /** Lancement d'applications non supporté. */
      UNSUPPORTED_LAUNCH,

      /** Lancement du navigateur non supporté. */
      UNSUPPORTED_BROWSER,

      /** Navigateur par défaut introuvable. */
      UNREACHABLE_BROWSER,

      /** Adresse du lien incorrecte. */
      INVALID_LINK
   }

   /** Propriétés du lien. */
   private LinkProperties linkProps;


   /**
    * <p>Constructeur.</p>
    * @param text Le texte du lien.
    * @param link Le lien associé.
    */
   public GLinkLabel(String text, String link)
   {
      this(new LinkProperties(text, link), null);
   }


   /**
    * <p>Constructeur.</p>
    * @param text Le texte du lien.
    * @param link Le lien associé.
    * @param icon L'icône associée.
    */
   public GLinkLabel(String text, String link, Icon icon)
   {
      this(new LinkProperties(text, link), icon);
   }


   /**
    * <p>Constructeur.</p>
    * @param linkProps Les propriétés du lien.
    */
   public GLinkLabel(LinkProperties linkProps)
   {
      this(linkProps, null);
   }


   /**
    * <p>Constructeur.</p>
    * @param linkProps Les propriétés du lien.
    * @param icon L'icône associée.
    */
   public GLinkLabel(LinkProperties linkProps, Icon icon)
   {
      this.linkProps = linkProps;
      setIcon(icon);
      initComponent();
   }


   /**
    * <p>Initialisation du composant.</p>
    */
   private void initComponent()
   {
      super.setText(computeLink(this.linkProps.getLinkColor(), this.linkProps.isLinkUnderlined()));
      setHorizontalAlignment(SwingConstants.LEADING);
      updateUI();
      setAlignmentX(LEFT_ALIGNMENT);
      setToolTipText(this.linkProps.getLink());
      setCursor(new Cursor(Cursor.HAND_CURSOR));
      addMouseListener(this);
   }


   /**
    * <p>Lance le navigateur par défaut avec l'adresse du lien.</p>
    */
   private void browse()
   {
      if(Desktop.isDesktopSupported())
      {
         Desktop desktop = Desktop.getDesktop();
         if(desktop.isSupported(Desktop.Action.BROWSE))
         {
            try
            {
               desktop.browse(new URI(this.linkProps.getLink()));
               if(!this.linkProps.isVisited())
               {
                  this.linkProps.setVisited(true);
               }
               fireLinkBrowsed(BrowseStatus.SUCCESS);
            }
            catch(IOException e)
            {
               fireLinkBrowsed(BrowseStatus.UNREACHABLE_BROWSER);
            }
            catch(URISyntaxException e)
            {
               fireLinkBrowsed(BrowseStatus.INVALID_LINK);
            }
         }
         else
         {
            fireLinkBrowsed(BrowseStatus.UNSUPPORTED_BROWSER);
         }
      }
      else
      {
         fireLinkBrowsed(BrowseStatus.UNSUPPORTED_LAUNCH);
      }
   }


   /**
    * <p>Modifie le texte du lien.</p>
    * @param text La nouvelle valeur du texte du lien.
    */
   @Override
   public void setText(String text)
   {
      if(this.linkProps != null)
      {
         this.linkProps.setText(text);
         String linkText = computeLink(this.linkProps.getLinkColor(), this.linkProps
               .isLinkUnderlined());
         super.setText(linkText);
      }
   }


   /**
    * <p>Renvoie l'adresse du lien.</p>
    * @return Retourne l'adresse du lien.
    */
   public String getLink()
   {
      return this.linkProps.getLink();
   }


   /**
    * <p>Modifie l'adresse du lien.</p>
    * @param link La nouvelle adresse du lien.
    */
   public void setLink(String link)
   {
      this.linkProps.setLink(link);
      setToolTipText(link);
   }


   /**
    * <p>Construit une représentation du lien.</p>
    * @param linkColor La couleur du lien
    * @param isUnderlined Indique si le lien est souligné.
    * @return Le texte permettant de représenter un lien.
    */
   private String computeLink(Color linkColor, boolean isUnderlined)
   {
      StringBuilder temp = new StringBuilder();
      temp.append("<html>");
      temp.append("<font color=\"#");
      temp.append(Integer.toHexString(linkColor.getRGB()).substring(2));
      temp.append("\">");
      if(isUnderlined)
      {
         temp.append("<u>");
      }
      temp.append(this.linkProps.getText());
      if(isUnderlined)
      {
         temp.append("</u>");
      }
      temp.append("</font>");
      temp.append("</html>");

      return temp.toString();
   }


   /**
    * <p>Ajoute un composant dans la liste de ceux qui veulent être informés d'un événement de lien
    * parcouru.</p>
    * @param l L'écouteur de lien parcouru.
    */
   public void addLinkListener(LinkListener l)
   {
      this.listenerList.add(LinkListener.class, l);
   }


   /**
    * <p>Supprime un composant de la liste de ceux qui veulent être informés d'un événement de lien
    * parcouru.</p>
    * @param l L'écouteur de lien parcouru.
    */
   public void removeLinkListener(LinkListener l)
   {
      this.listenerList.remove(LinkListener.class, l);
   }


   /**
    * <p>Lance un évènement à tous les objets écouteurs.</p>
    * @param status Le statut du parcours du lien.
    */
   protected void fireLinkBrowsed(BrowseStatus status)
   {
      LinkListener[] listeners = this.listenerList.getListeners(LinkListener.class);
      LinkEvent e = new LinkEvent(this, this.getLink(), status);
      for(LinkListener l : listeners)
      {
         l.linkBrowsed(e);
      }
   }


   /**
    * <p>Invoqué lorsque le bouton de la souris a été cliqué. (pressé et relaché) sur le
    * composant.</p>
    * @param e Un événement qui indique qu'un action de souris s'est produite sur un composant.
    */
   public void mouseClicked(MouseEvent e)
   {
      // Aucun traitement.
   }


   /**
    * <p>Invoqué lorsque le bouton de la souris a été pressé sur le composant.</p>
    * @param e Un événement qui indique qu'un action de souris s'est produite sur un composant.
    */
   public void mousePressed(MouseEvent e)
   {
      // Lancement du navigateur dans un autre thread afin de ne pas bloquer l'IHM.
      Thread browseThread = new Thread("Thread Browse")
      {
         /**
          * <p>Lance l'exécution du parcours du lien.</p>
          */
         @Override
         public void run()
         {
            browse();
         }
      };
      browseThread.start();
   }


   /**
    * <p>Invoqué lorsque le bouton de la souris a été relaché sur le composant.</p>
    * @param e Un événement qui indique qu'un action de souris s'est produite sur un composant.
    */
   public void mouseReleased(MouseEvent e)
   {
      // Aucun traitement.
   }


   /**
    * <p>Invoqué lorsque la souris entre dans le composant.</p>
    * @param e Un événement qui indique qu'un action de souris s'est produite sur un composant.
    */
   public void mouseEntered(MouseEvent e)
   {
      String linkText = computeLink(this.linkProps.getHoverLinkColor(), this.linkProps
            .isHoverLinkUnderlined());
      super.setText(linkText);
   }


   /**
    * <p>Invoqué lorsque la souris sort du composant.</p>
    * @param e Un événement qui indique qu'un action de souris s'est produite sur un composant.
    */
   public void mouseExited(MouseEvent e)
   {
      String linkText;
      if(this.linkProps.isVisited())
      {
         linkText = computeLink(this.linkProps.getVisitedLinkColor(), this.linkProps
               .isVisitedLinkUnderlined());
         super.setText(linkText);
      }
      else
      {
         linkText = computeLink(this.linkProps.getLinkColor(), this.linkProps.isLinkUnderlined());
         super.setText(linkText);
      }
   }
}
